using System;
using System.Collections;
using System.Configuration;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Serialization;
using SqlRunner.Events;
using SqlRunner.UserInterfaceStatus;
using SqlRunner.util;
using SQLServices.Log;
using SQLServices.Runners;
using SQLServices.Runners.Events;
using SQLServices.Version;
using SQLServices.Runners.Services;
using SQLServices.Runners.Util;

namespace SqlRunner
{
	/// <summary>
	/// Singleton instance class that contains methods that help the FormMain.
	/// </summary>
	public class FormMainHelper
	{
		#region Private Instances

		private static FormMainHelper singletonInstance;
		private FormMain formInstance;
		private ArrayList folderList;

		/// <summary>
		/// Provides a way to update the UI when the Runner is executing
		/// SQL script files
		/// </summary>
		private AddLogEventHandler addLogEvent;

		/// <summary>
		/// Indicates that a script was executed
		/// </summary>
		private SuccessScriptHandler scriptRunEvent;

		/// <summary>
		/// Indicates that an error took place when a script was run
		/// </summary>
		private ErrorScriptHandler scriptErrorEvent;

		/// <summary>
		/// Indicates that a new message was created by the runner to be
		/// displayed by the UI
		/// </summary>
		private AddMessageHandler addMessageEvent;
		
		#endregion
		#region Properties
		
		public ArrayList FolderList
		{
			get { return folderList; }
			set { folderList = value; }
		}

		#endregion
		#region Singleton Methods

		private FormMainHelper(FormMain formInstance)
		{
			this.formInstance = formInstance;
			addMessageEvent = new AddMessageHandler(formInstance.setComment);
			scriptErrorEvent = new ErrorScriptHandler(formInstance.increaseErrorCounter);
			scriptRunEvent = new SuccessScriptHandler(formInstance.increaseTotalCounter);
			addLogEvent = new AddLogEventHandler(formInstance.addLog);
		}

		public static FormMainHelper Instance
		{
			get
			{
				if (singletonInstance != null) return singletonInstance;
				throw new ArgumentException("Need to pass a frmMain helper instance");
			}
		}

		public static FormMainHelper GetInstance(FormMain formInstance)
		{
			if ( singletonInstance != null ) return singletonInstance;
			singletonInstance = new FormMainHelper(formInstance);
			return singletonInstance;
		}

		#endregion
		#region Public Methods

		// Use it to save the last selected path
		public static string LastPath = "";
		private Thread _scripts;

		public int TotalFiles()
		{
			int files = 0;
			foreach (ScriptFolder folder in folderList)
			{
				OnProgressMsgCreated(this, 
					new ProgressMsgCreatedEventArgs(
						"Reading script files at {0} folder", 
						folder.Dir.FullName));
				files += 
					(new FileCounter(folder.Dir.FullName, "*.sql")).TotalFiles(folder.RunSubfolders);
			}
			return files;
		}
		public void runScripts()
		{
			IRunner runner = ServiceContainer.CreateRunner(FolderList);
			HookEvents(runner);
			ServiceContainer.Logger.LogMessage("frmMainHelper - runScripts method was called");
			// Start a new thread to run the scripts so the UI can be refreshed
			_scripts = new Thread(new ThreadStart(runner.RunScripts));
			_scripts.Name = "Runner";
			_scripts.IsBackground = true;
			_scripts.Start();
		}

		public RunnerProperties ReadProperties(FileSystemInfo file)
		{
			XmlSerializer aSerializer = new XmlSerializer(typeof(RunnerProperties));
			FileStream aReader = new FileStream(file.Name, FileMode.Open);
			RunnerProperties aProperties = (RunnerProperties) aSerializer.Deserialize(aReader);
			aReader.Close();
			return aProperties;			
		}

		public void WriteProperties(RunnerSettings runnerSettings, string serverName, 
			string databaseName, string userId)
		{	
			RunnerProperties runnerProperties = runnerSettings.FindSetPropertiesByKeyField(serverName);
			if ( runnerProperties == null )
			{
				runnerProperties = new RunnerProperties();
				ArrayList aList = new ArrayList(4);
				RunnerProperty property = new RunnerProperty("servername", serverName);
				aList.Add(property);
				property = new RunnerProperty("databasename");
				aList.Add(property);
				property = new RunnerProperty("uid");
				aList.Add(property);
				property = new RunnerProperty("path");
				aList.Add(property);
				runnerProperties.Items = (RunnerProperty[]) aList.ToArray(typeof(RunnerProperty));
				runnerSettings.Properties.Add(runnerProperties);
			}
			runnerProperties.FindProperty("databasename").value = databaseName;
			runnerProperties.FindProperty("uid").value = userId;
			runnerProperties.FindProperty("path").value = LastPath;

			runnerSettings.key = "servername";
			runnerSettings.defaultProperties = serverName;
			runnerSettings.Serialize(ConfigurationSettings.AppSettings["RunnerPropertiesFile"]);
		}


		public ArrayList GetFolderList(CheckedListBox folders, IWin32Window sender)
		{
			ArrayList folderlist = new ArrayList();
			foreach (object item in folders.Items)
			{
				// Get list dir
				DirectoryInfo aDir = new DirectoryInfo(item.ToString());
			
				// Check if exist
				if (aDir.Exists)
				{
					ScriptFolder folder = new ScriptFolder(aDir, folders.CheckedItems.Contains(item));
					folderlist.Add(folder);
				}
				else
				{
					MessageBox.Show(sender, String.Format("The {0} does not exists", item.ToString()));
				}
			}
			return folderlist;
		}

		public TreeNode GetParentNode(TreeNodeCollection nodes, IPathItem item)
		{
			// check if node exists
			TreeNode parent = FindNodeByName(nodes, item);
			if (parent == null)
			{
				parent = nodes.Add(item.Name);
			}
			// check if the next node is a subfolder
			if (item.NextNode != null)
			{
				return GetParentNode(parent.Nodes, item.NextNode);
			}
			return parent;
		}

		public void ConnectToSql(ComboBox comboBox, string server, 
			string userName, string password, bool trusted, RunnerProperties properties)
		{
			ServiceContainer.SQLConnection.OpenConnection(server, userName, password, trusted);
			comboBox.Items.Clear();
			comboBox.Items.AddRange(ServiceContainer.SQLConnection.ListOfDatabases().ToArray());
			FindDefaultDatabase(properties, comboBox);
		}

		public StringBuilder ParseErrorMessage(string[] strPhrases)
		{
			StringBuilder strBuilder = new StringBuilder();
			char[] rc = {(char)13, (char)10};
			for (int i = 0; i < strPhrases.Length ; i++)
			{
				char[] separators = {'[',']'};
				String[] words = strPhrases[i].Split(separators);
				for (int j = 0; j < words.Length ; j++)
				{
					if (strBuilder.Length > 0 && words[j].Length > 0)
						strBuilder.Append(rc);
					strBuilder.Append(words[j]);
				}
			}
			return strBuilder;
		}

		public void AddFolders(ArrayList folders, CheckedListBox listBox)
		{
			for (int i = 0; i < folders.Count; i++)
			{
				string path = ((DirectoryInfo)folders[i]).FullName;
				if ( !listBox.Items.Contains(path.ToLower()) )
					listBox.Items.Add(path.ToLower());
			}
		}

		public void CancelRunner()
		{
			if ( _scripts != null && _scripts.IsAlive )
			{
				ServiceContainer.Logger.LogMessage(
					String.Format("Script batch execution was cancelled by the user"), 
					MsgLevel.Info);
				_scripts.Interrupt();
				formInstance.ProcessStatus = Status.Cancel;
			}
		}

		public void AbortRunner()
		{
			if ( _scripts !=null && _scripts.IsAlive )
			{
				ServiceContainer.Logger.LogMessage(
					String.Format("Script batch execution was aborted by the application"), 
					MsgLevel.Info);
				_scripts.Abort();
				formInstance.ProcessStatus = Status.Cancel;
			}
		}
		
		#endregion
		#region Private Methods

		#region Event Methods

		private void HookEvents(IRunner runner)
		{
			runner.ProgressMsgCreated += new ProgressMsgCreatedEventHandler(OnProgressMsgCreated);
			runner.ScriptExecError += new ScriptExecErrorEventHandler(OnScriptExecError);
			runner.ScriptExecuted +=new ScriptExecutedEventHandler(OnScriptExecuted);
			runner.ExecutionFinished +=new ExecutionFinishedEventHandler(OnExecutionFinished);
		}

		private void UnHookEvents(IRunner runner)
		{
			runner.ProgressMsgCreated -= new ProgressMsgCreatedEventHandler(OnProgressMsgCreated);
			runner.ScriptExecError -= new ScriptExecErrorEventHandler(OnScriptExecError);
			runner.ScriptExecuted -=new ScriptExecutedEventHandler(OnScriptExecuted);
			runner.ExecutionFinished -=new ExecutionFinishedEventHandler(OnExecutionFinished);
		}

		private void OnExecutionFinished(object sender, ExecutionFinishedEventArgs e)
		{
			if ( formInstance.ProcessStatus != Status.Cancel )
			{
				formInstance.setComment("All scripts were executed");
			}
			else
			{
				formInstance.setComment("Script processor was aborted and not all files were executed");
			}
			formInstance.ProcessStatus = Status.DBSelected;
			UnHookEvents((Runner) sender);
		}

		private void OnScriptExecError(object sender, ScriptExecutionErrorEventArgs e)
		{
			formInstance.BeginInvoke(scriptErrorEvent);
			formInstance.BeginInvoke(scriptRunEvent);
			ServiceContainer.EventLogger.AddError(e.FileName, e.ErrorMsg);
			object[] p = new object[] {ServiceContainer.EventLogger.AddError(e.FileName, e.ErrorMsg)};
			ServiceContainer.Logger.LogMessage(
				String.Format("Script Error - File: [{0}]: {1}", e.FileName, e.ErrorMsg), 
				MsgLevel.Info);
			formInstance.BeginInvoke(addLogEvent, p);
		}

		private void OnProgressMsgCreated(object sender, ProgressMsgCreatedEventArgs e)
		{
			ServiceContainer.Logger.LogMessage("frmMainHelper - OnProgressMsgCreated method was called");
			object[] p = new object[]{e.Message, new object[]{} };
			formInstance.BeginInvoke(addMessageEvent, p);
		}

		private void OnScriptExecuted(object sender, ScriptExecutedEventArgs e)
		{
			ServiceContainer.Logger.LogMessage("frmMainHelper - OnScriptExecuted method was called");
			formInstance.BeginInvoke(scriptRunEvent);
			object[] p = new object[] {ServiceContainer.EventLogger.AddOk(e.FileName)};
			formInstance.BeginInvoke(addLogEvent, p);
		}

		#endregion

		// Finds node by the node caption
		private TreeNode FindNodeByName(TreeNodeCollection coll, IPathItem path)
		{
			if (coll == null) return null;
			foreach (TreeNode aNode in coll)
			{
				// we are looking for subfolder nodes so only
				// interested in nodes with subnodes
				if (aNode.Nodes.Count > 0)
				{
					// if path and node have same name
					if (aNode.Text.Equals(path.Name))
					{
						return aNode;
					}
				}
			}
			return null;
		}

		private void FindDefaultDatabase(RunnerProperties properties, ComboBox cmbDatabases)
		{	
			if (properties != null)
			{
				RunnerProperty property = properties.FindProperty("databasename");
				string strDB = null;
				if (property != null) strDB = property.value;
			
				if (strDB != null && cmbDatabases.Items.Contains(strDB))
				{
					cmbDatabases.SelectedIndex = cmbDatabases.Items.IndexOf(strDB);
					return;
				}
			}
			if (cmbDatabases.Items.Count > 0) cmbDatabases.SelectedIndex = 0;
		}

		#endregion

	}
}
